home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’96
/
Venus
/
Dialog.cc
next >
Wrap
Text File
|
1996-06-22
|
12KB
|
314 lines
/*
***********************************************************************
*
*
* Generic Dialog Class
* Implementing a few very basic functions
* messing with items, etc
*
*
***********************************************************************
*/
#include "Dialog.h"
#include "mymenv.h"
/*
*-------------------------------------------------------------------------------------
* Dialog construction/destruction
*
* Dialog constructors create a (modeless) dialog (from a DLOG resource) and set
* up the initial state of the controls and user items.
* Note that the dialog is ought to be specified as initially hidden
* in the resource template. So that constructors of the derived class could get a chance
* to set up user item drawing routines, or initialize/move around custom controls.
* Call show() when ready (from an upper-level constructor)
*/
// Create a color dialog from a resource template
ModelessDialog::ModelessDialog(const short resource_id)
{
assert( this_window = (WindowPtr)GetNewDialog(resource_id, nil, (WindowPtr)-1) );
SetWRefCon(this_window,(long)this); // Store the ptr to our class in RefCon
}
// A kludge for a virtual destructor
void ModelessDialog::destroy_it(void)
{
assert( this_window != 0 );
DisposeDialog((DialogPtr)this_window);
this_window = 0;
}
/*
*-------------------------------------------------------------------------------------
* Event Handling
*/
Boolean ModelessDialog::handle_event(const EventRecord& the_event)
{
if( !IsDialogEvent(&the_event) )
return ScreenWindow::handle_event(the_event); // Not a dialog event, let ScreenWindow
// handle that. Note that, say, dragging
// of a window is reported as NOT a dialog
// event
if( the_event.what == activateEvt && (WindowPtr)(the_event.message) == our_window() )
handle_activate(the_event.modifiers & activeFlag);
DialogPtr dialog_hit;
short item_hit;
if( !DialogSelect(&the_event, &dialog_hit, &item_hit) )
return TRUE; // assume that DialogSelect has handled the Event
if( dialog_hit != this_window )
return TRUE; // Not our dialog event, wait for the other event
// The following stmt means that if a generic_item_hit
// handle didn't handle the click completely, a more
// specific handle would be given a chance
return generic_item_hit(item_hit) || handle_item_hit(item_hit);
}
Boolean ModelessDialog::handle_null_event(const long event_time)
{
return TRUE;
}
// Handle suspend/resume events
Boolean ModelessDialog::handle_activate(Boolean onoff)
{
return TRUE;
}
// Figure out if our item was hit and give an object
// click handler a chance to handle the click.
// Return TRUE if the click is _completely_
// handled, return FALSE if additional attention
// needed (say, a control's value was changed)
Boolean ModelessDialog::generic_item_hit(const int item_no)
{
Handle item_handle;
short item_type;
Rect item_rect;
GetDItem((DialogPtr)this_window, item_no, &item_type, &item_handle, &item_rect);
if( (item_type & ctrlItem) != ctrlItem || item_handle == nil )
return FALSE;
BasicControl * our_control_ptr = (BasicControl *)GetControlReference((ControlHandle)item_handle);
return our_control_ptr != nil && our_control_ptr->is_our_control() &&
our_control_ptr->handle_click();
}
/*
*-------------------------------------------------------------------------------------
* Utilities to handle dialog items
*/
// Take a "ref" of a dialog item
ModelessDialog::Item::Item(const ModelessDialog& the_dialog, const int item_no)
{
GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &item_rect);
assert( item_handle != nil );
}
// Take a "ref" of a dialog _control_ item, and
// make sure that it's a control indeed
ModelessDialog::ControlItem::ControlItem(const ModelessDialog& the_dialog, const int item_no)
: ModelessDialog::Item(the_dialog,item_no)
{
assert( (item_type & ctrlItem) == ctrlItem );
}
// Take a "ref" of a dialog _text_ item, and
// make sure that it's text indeed
ModelessDialog::TextItem::TextItem(const ModelessDialog& the_dialog, const int item_no)
: ModelessDialog::Item(the_dialog,item_no)
{
assert( (item_type & statText) == statText );
}
// Draw a new text in an item
void ModelessDialog::TextItem::draw(const Str255 str)
{
SetDialogItemText(item_handle,str);
}
// Make a TextItem when the text is an ASCII representation
// of a number
ModelessDialog::NumberItem::NumberItem(const ModelessDialog& the_dialog, const int item_no)
: ModelessDialog::TextItem(the_dialog,item_no)
{
}
// Draw a new number in an item
void ModelessDialog::NumberItem::draw(const int number)
{
Str255 temp_str;
NumToString(number,temp_str);
TextItem::draw(temp_str);
}
// Set a new value of a control, clipped to
// its [min,max] range
void BasicControl::set_value(const int new_value)
{
ControlHandle ctl_handle = our_control();
const int max_val = GetControlMaximum(ctl_handle);
SetControlValue(ctl_handle, new_value > max_val ? max_val :
new_value < GetControlMinimum(ctl_handle) ?
GetControlMinimum(ctl_handle) : new_value );
}
/*
*-------------------------------------------------------------------------------------
* Patching the CDEF
*
* In handling a scrollbar, we need to tell the Control Manager how to change the control
* value when PgUp/PgDn/ArrowUp/ArrowDn areas of a scrollbar got clicked. Normally it's
* accomplished by a track Action procedure, which is called by TrackControl(). In our
* case, TrackControl() is called by DialogSelect. We could supply our Action procedure
* by putting a pointer to it in a corresponding field of the ControlRecord. But it
* doesn't work! The reason is that if an action procedure is specified this way, it's
* called under two different circumstances: mouse click in PgUp/PgDn/ArrowUp/ArrowDn areas,
* and a mouse click in the thumb (indicator). In the former case, the action procedure is
* passed two parameters, in the latter case, no parameters. Since the action procedure
* is a 'pascal' one, passing it zero parameters when it expects two, or vice versa
* messes up the stack on return (with all the disastorous consequences). As IM itself
* admits, the only way to specify a "generic action procedure" is to modify a CDEF,
* which we're going to do.
* Note, the usual way of handling this situation is calling FindControl(), TrackControl()
* etc before DialogSelect(). In a sense, the usual hack is to duplicate DialogSelect.
* IMHO, patching CDEF seems more logical.
*
* Note, in patching a CDEF, we assume that BasicControl is a scrollbar-type thing,
* that's why real_defproc_handle, patch_defproc_handle etc are declared static.
* Although it's damn easy to lift this limitation.
*
* Note, that in installing our patch, we can't just barge into CDEF handle and change
* the pointer. ResourceManager, who created that CDEF handle, wouldn't like that.
* So we need to allocate our own handle, stash a pointer (universal pointer) of our patch
* into it, and put the new handle into CDEF. DisposeControl would dispose of that handle
* later.
*/
// Late constructor for a basic control
// A regular constructor won't cut it, since it would
// be called before the container (the dialog)
// gets constructed
void BasicControl::bind(const ModelessDialog::ControlItem& item)
{
this_control = (ControlHandle)item;
SetControlReference(this_control,(long)this);
// SetControlAction(this_control,track_action_relay_upp);
// SetControlAction(this_control,(ControlActionUPP)(-1));
if( real_defproc_handle == nil ) // save the old CDEF handle and initialize a new one
{
real_defproc_handle = (ControlDefProcPtr *)(*this_control)->contrlDefProc;
patch_defproc_handle = (ControlDefProcPtr *)NewHandle(sizeof(ControlDefUPP));
HLock((Handle)patch_defproc_handle);
*patch_defproc_handle = (ControlDefProcPtr)defproc_patch_upp;
} // Install our CDEF patch
assert( real_defproc_handle == (ControlDefProcPtr *)(*this_control)->contrlDefProc );
(*this_control)->contrlDefProc = (Handle)patch_defproc_handle;
}
// This is an on-the-fly patch to a scroll bar control definition
// function.
// The patch itself calls the standard CDEF, and, if it finds out
// that thumb had been moved and/or gray areas/arrows were clicked,
// calls the object's track action procedure
// We assume that the pointer to the BasicControl Object is stored
// in the refcon
pascal SInt32 BasicControl::defproc_patch
(SInt16 var_code, ControlRef the_control, ControlDefProcMessage message, SInt32 param)
{
SInt32 def_result = CallControlDefProc(*real_defproc_handle,var_code,the_control,message,param);
if( (message == drawCntl && (short)param == 129) // indicator to be redrawn
|| (message == testCntl && def_result != 0) ) // a scrollbar area might've been clicked
if( !StillDown() ) // mouse was released, that is, the click is completed
{
BasicControl * this_ptr = (BasicControl *)((*the_control)->contrlRfCon);
assert( this_ptr != nil && this_ptr->is_our_control() );
this_ptr->track_action(def_result);
}
return def_result;
}
ControlDefProcPtr * BasicControl::real_defproc_handle = 0;
ControlDefProcPtr * BasicControl::patch_defproc_handle = 0;
ControlDefUPP BasicControl::defproc_patch_upp
= NewControlDefProc(defproc_patch); // Universal pointer to BasicControl::defproc_patch
#if 0
// This is a relay between the Control Manager and real action
// tracking procedure
// We assume that the pointer to the BasicControl Object is stored
// in the refcon
pascal void BasicControl::track_action_relay(ControlHandle the_control, unsigned short part_no)
{
DebugStr("\pIn track action relay");
if( part_no >= inThumb ) // Note when the mouse is clicked in the indicator,
return; // parameters are probably invalid
BasicControl * this_ptr = (BasicControl *)GetControlReference(the_control);
assert( this_ptr != nil && this_ptr->is_our_control() );
this_ptr->track_action(part_no);
}
ControlActionUPP BasicControl::track_action_relay_upp
= NewControlActionProc(track_action_relay); // Universal pointer to the user_item_univ_handler
#endif
/*
*-------------------------------------------------------------------------------------
* Handling User items
*/
// This is a relay between the Dialog Manager and a real user item
// drawing procedure
// We assume that the pointer to the Dialog Object is stored
// in the refcon
pascal void UserItem::user_item_relay_handler(WindowPtr the_dialog, short the_item)
{
ModelessDialog * this_ptr = (ModelessDialog *)GetWRefCon(the_dialog);
assert( this_ptr != nil );
this_ptr->draw_user_item(the_item);
}
UserItemUPP UserItem::user_item_relay_handler_upp
= NewUserItemProc(user_item_relay_handler); // Universal pointer to the user_item_univ_handler
// Binding and activating a user item
// It sets up a relay function as an item handle
void UserItem::bind(const ModelessDialog& the_dialog, const int item_no)
{
Handle item_handle;
short item_type;
GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &rect);
assert( item_handle == nil ); // make sure it hasn't been set up yet
SetDItem((DialogPtr)the_dialog.our_window(), item_no, item_type, (Handle)user_item_relay_handler_upp, &rect);
}
#if 0
// Standard way to draw a border around the default button.
void ModelessDialog::draw_default_item_border(const int item_no)
{
const Item item(*this,item_no);
PenSize(3,3);
InsetRect((Rect *)(const Rect *)item, -4, -4);
FrameRoundRect((const Rect *)item, 16,16);
}
#endif